/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grafogen;


import grafogen.TablaDecision;
import graphViz.GraphViz;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Vector;

/**
 *
 * @author laura
 */ 
public class CtrolGrafo implements Serializable{
    
    private static Grafo grafo = null;

    
    /**
     * Este método agrega un nuevo nodo al grafo
     * 
     * @param codigo
     *            Código del nodo
     * @param nombre
     *            El nombre del nodo
     * @return true si el nodo se pudo agregar, false si el nodo no se agregó
     */
    public boolean agregarNodo(Integer codigo, String nombre, String tipo) {
            return grafo.agregarNodo(new Nodo(codigo, nombre, tipo));
    }

    /**
     * Este método agrega una nueva arista al grafo
     * 
     * @param codigo
     *            Código de la arista
     * @param tipo
     *            El tipo de la arista
     * @param operador
     *            Nodo operador
     * @param destino
     *            Nodo destino
     * @return true si el nodo se pudo agregar, false si el nodo no se agregó
     */
    public boolean agregarArista(Integer codigoAri, String tipoAri, Nodo origen,
                    Nodo destino) {
            return grafo.agregarArista(new Arista(codigoAri, origen, destino));
    }


    public static Grafo unionGraf(Nodo nodo, Grafo grafoActual, Grafo grafoUnion){
        ArrayList<Arista> listaAristas = new ArrayList<Arista>();
        listaAristas = grafoActual.buscarAristaDest(nodo);
        int cantAristas = 0;

        if("operador".equals(nodo.getTipo())){
            int cantNodos = grafoUnion.getNodos().size();
            nodo.setCodigoN(cantNodos+1); //cambiamos la numeracion para agregar al nuevo grafo
            grafoUnion.agregarNodo(nodo);

            if(grafoUnion.getUltimoNodo() != null){
                cantAristas = grafoUnion.getAristas().size();
                Arista arista = new Arista(cantAristas+1, nodo, grafoUnion.getUltimoNodo());
                grafoUnion.agregarArista(arista);
            }
            
            grafoUnion.setUltimoNodo(nodo);

        }else{
            if(!grafoUnion.existeNodo(nodo)){
               //no existe el nodo, lo agrego al grafo
               int cantNodos = grafoUnion.getNodos().size();
               nodo.setCodigoN(cantNodos+1); //cambiamos la numeracion para agregar al nuevo grafo
               grafoUnion.agregarNodo(nodo);
            }else{
                grafoUnion.setUnion(true); //este grafo es parte de otro grafo
            }

            //si ultimo nodo es distinto de null, agrega arista sfdz 31/08/2012
            if(grafoUnion.getUltimoNodo() != null){
                cantAristas = grafoUnion.getAristas().size();
                Arista arista = new Arista(cantAristas+1, nodo, grafoUnion.getUltimoNodo());
                grafoUnion.agregarArista(arista);
            }
           
        }
        
//System.out.println("grafoActual:"+grafoActual.getAristas());


        if(listaAristas.size() > 0){
            for(int i=0; i < listaAristas.size(); i++){
                Arista aristaActual = listaAristas.get(i);
                Nodo nodoOrigen = aristaActual.getOrigen();             
                grafoUnion = unionGraf(nodoOrigen, grafoActual, grafoUnion);
                grafoUnion.setUltimoNodo(nodo);
            }
        }

        return grafoUnion;
    }



    public static void imprimir(Grafo grafoUnion){
        ArrayList<Arista> listaAristas = new ArrayList<Arista>();
        Nodo nodo = grafoUnion.getUltimoNodo();
        listaAristas = grafoUnion.buscarAristaDest(nodo);


        if(listaAristas.size() > 0){
            for(int i=0; i < listaAristas.size(); i++){
                Arista aristaActual = listaAristas.get(i);
                Nodo nodoOrigen = aristaActual.getOrigen();
                imprimir(grafoUnion);
            }
        }
    }


    public static Grafo unionPrincipalGraf(Grafo grafo, Grafo grafoUnion){
        Grafo nuevoGrafo = new Grafo();

        ArrayList<Arista> listaAristaEfectos = new ArrayList<Arista>();
        listaAristaEfectos = grafo.buscarAristaDestEfecto(); //busca aristas que tienen como destino al efecto del grafo
        if(listaAristaEfectos.size() > 0){
            for(Arista arista: listaAristaEfectos){ //aristas que apuntan a efectos del grafo
                
                Nodo nodoOrigenEfecto = arista.getOrigen();
                grafoUnion.agregarArista(arista); //agrego ya la arista que apunta al efecto
                
                Nodo nodoEfecto = arista.getDestino();
                //123miro: vemos si el efecto es el nodo comun
                if (grafoUnion.existeNodo(nodoEfecto)) {
                    grafoUnion.setUnion(true); //este grafo es parte de otro grafo
                } else {
                    grafoUnion.agregarNodo(nodoEfecto);
                }
                
                
                //nunca puede haber algo detras de una causa, si la causa es origen del efecto, existe una sola arista
                if( "causa".equals(nodoOrigenEfecto.getTipo()) ){
                    //123miro: aca estaba antes
                        
                    if(grafoUnion.existeNodo(nodoOrigenEfecto)){
                        grafoUnion.setUnion(true); //este grafo es parte de otro grafo
                    }else{
                        grafoUnion.agregarNodo(nodoOrigenEfecto);
                    }
                    
                    //debemos agregar la arista
                    //el nodo 
                    //y no cambiar su ultimo nodo. termina todo ya
                    nuevoGrafo = grafoUnion;
                }else{
                    //si tiene mas de una arista tiene un tratamiento especial
                    grafoUnion.setUltimoNodo(null);
                    nuevoGrafo = unionGraf(nodoOrigenEfecto,grafo,grafoUnion);
                }
                
            }
        }

        return nuevoGrafo;
    }

    public static void graficar(Grafo grafo, int nroGrafo) {
        GraphViz gv = new GraphViz();
        gv.addln(gv.start_graph());

        for(Arista arista : grafo.getAristas()){
            
            String origen = arista.getOrigen().getDescripcion();
            
            if("operador".equals( arista.getOrigen().getTipo())){
                origen = origen + arista.getOrigen().getCodigoN().intValue(); 
            }else if("causa".equals( arista.getOrigen().getTipo())){
                origen = "C"+arista.getOrigen().getCodigoN().intValue(); 
            }else if("efecto".equals( arista.getOrigen().getTipo())){
                origen = "E"+arista.getOrigen().getCodigoN().intValue(); 
            }
            
            String destino = arista.getDestino().getDescripcion();
            
            if("operador".equals( arista.getDestino().getTipo() )){
                destino = destino + arista.getDestino().getCodigoN().intValue();
            }else if("causa".equals( arista.getDestino().getTipo() )){
                destino = "C" + arista.getDestino().getCodigoN().intValue();
            }else if("efecto".equals( arista.getDestino().getTipo() )){
                destino = "E" + arista.getDestino().getCodigoN().intValue();
            }
            
            
            gv.addln(origen+" -> "+destino+";");
        }
        gv.addln(gv.end_graph());
        System.out.println(gv.getDotSource());

//      String type = "gif";
//      String type = "dot";
//      String type = "fig";    // open with xfig
//      String type = "pdf";
//      String type = "ps";
//      String type = "svg";    // open with inkscape
        String type = "png";
//      String type = "plain";
//      File out = new File("/tmp/out." + type);   // Linux
        //System.out.println(System.getProperty("user.dir")+"\\tmp\\out." + type);
        File out = new File(System.getProperty("user.dir") + "\\imagenesTmp\\out"+nroGrafo+"." + type);    // Windows
        gv.writeGraphToFile(gv.getGraph(gv.getDotSource(), type), out);
    }


    public static ArrayList<Vector> calcularValoresVerdad(String operador, String resultadoEsperado) {
        ArrayList<Vector> lista = new ArrayList<Vector>();

        /*
         * OR,  0 -> {(0,0)}
         */
        if ("OR".equalsIgnoreCase(operador) && "0".equalsIgnoreCase(resultadoEsperado)) {
            Vector v = new Vector();
            v.add(0, "0");
            v.add(1, "0");
            lista.add(v);

        } else if ("OR".equalsIgnoreCase(operador) && "1".equalsIgnoreCase(resultadoEsperado)) {
            /*
             * OR,  1 -> {(0,1),(1,0)}
             */
            Vector v = new Vector();
            v.add(0, "0");
            v.add(1, "1");            
            lista.add(v);

            v = new Vector();
            v.add(0, "1");
            v.add(1, "0");
            lista.add(v);

        } else if ("AND".equalsIgnoreCase(operador) && "0".equalsIgnoreCase(resultadoEsperado)) {
            /*
             *  AND, 0 -> {(0,0),(1,0)}
             */
            Vector v = new Vector();
            v.add(0, "0");
            v.add(1, "0");
            lista.add(v);

            v = new Vector();
            v.add(0, "1");
            v.add(1, "0");
            lista.add(v);

        } else if ("AND".equalsIgnoreCase(operador) && "1".equalsIgnoreCase(resultadoEsperado)) {
            /*
             * AND, 1 -> {(1,1)}
             */
            Vector v = new Vector();
            v.add(0, "1");
            v.add(1, "1");
            lista.add(v);
        }

        return lista;
    }


    public static ArrayList<TablaDecision> armarTablaDecision(Grafo grafo){
        ArrayList<Nodo> listaEfectos = buscarEfectos(grafo);
        Integer cantCausas = buscarCausas(grafo);
        grafo.setCantidadCausas(cantCausas);

        ArrayList<TablaDecision> listaDeTablaDecision = new ArrayList<TablaDecision>();
               
        //recorrer los efectos
        for(int i=0; i < listaEfectos.size(); i++){
            //obtener las aristas que apuntan a ese efecto
            ArrayList<Arista> aristasDest = new ArrayList<Arista>();
            Nodo nodo = listaEfectos.get(i);
            aristasDest = grafo.buscarAristaDest(nodo);

            //la tabla de decision
            TablaDecision tablaDecision = new TablaDecision();
            
            Vector vectorCausasEfecto = new Vector();
            vectorCausasEfecto = obtenerVectorCausas(grafo);
            vectorCausasEfecto.set(vectorCausasEfecto.size()-1, "E"+nodo.getCodigoN() ); //agregamos el efecto al vector de causas
            
            tablaDecision.setCausasEfecto(vectorCausasEfecto);
            
            
            for(int j=0; j < aristasDest.size(); j++){
                Arista arista = aristasDest.get(j);
                Nodo nodoOrigen = arista.getOrigen();

                tablaDecision.setCodigoNodoRaiz(nodoOrigen.getCodigoN());

                obtenerTabla(nodoOrigen, grafo, tablaDecision, "1");
		tablaDecision.setCantidadVectores(tablaDecision.getTabla().size()); //actualizamos cuando sale de una recursividad,
                                                                                    //sabemos cuantos vectores hay en la tabla
                //CtrolGrafo.imprimirTabla(tablaDecision);
            }

            listaDeTablaDecision.add(tablaDecision);

            //uno siempre es el valor del nodo que va unido al efecto
            
        }

        return listaDeTablaDecision;

    }


    public static void obtenerTabla(Nodo nodo, Grafo grafo, TablaDecision tablaDecision, String resultadoEsperado) {
        //si es un operador hago todo esto
        if ("operador".equals(nodo.getTipo())) {

            //obtenemos los valores posibles del primer nodo que viene despues del efectoc
            ArrayList<Vector> listaValoresPosibles = new ArrayList<Vector>();
            listaValoresPosibles = calcularValoresVerdad(nodo.getDescripcion(), resultadoEsperado);

            //obtener aristas que tienen al nodo como destino
            ArrayList<Arista> aristasOrigen = new ArrayList<Arista>();
            aristasOrigen = grafo.buscarAristaDest(nodo);

            for (Vector valoresPosibles : listaValoresPosibles) {
                //le paso la primera combinacion de valores posibles

                for (int i = 0; i < valoresPosibles.size(); i++) {
                    String valor = (String) valoresPosibles.get(i);

                    if (aristasOrigen.size() > 0) {
                       //el primer valor para la primera arista
                       Nodo nodoHijo = aristasOrigen.get(i).getOrigen();

                       //llama recursivamente desde el nodo hijo con el valor esperado
                       obtenerTabla(nodoHijo, grafo, tablaDecision, valor);
                    }

                }


                if (tablaDecision.getCodigoNodoRaiz() == (nodo.getCodigoN())) {
                    if (tablaDecision.getTabla().size() > 0) {
                        tablaDecision.setCantidadVectores(new Integer(tablaDecision.getTabla().size()));
                    }
                }
            }
            
            completarTabla(tablaDecision);
            

        } else { //no es un operador es una causa
            //ver si ya tengo un vector creado, sino crear e insertar en tabla
            if ( (tablaDecision.getTabla().size() > 0) && (tablaDecision.getTabla().size() > tablaDecision.getCantidadVectores()) ) {

                boolean insertePrimero = false; //si en la primera iteracion inserte y en la siguiente la pos ya tenia valor, no creo otro vector

                for (int i = tablaDecision.getTabla().size()-1; i > tablaDecision.getCantidadVectores()-1; i--) {
                    Vector vector = tablaDecision.getTabla().get(i); //visito cada vector de la tabla
                    Integer posCausa = nodo.getCodigoN();
                    //si en la posicion de la causa (-1) ya existe un valor cargado creo otro vector
                    if (vector.elementAt(posCausa) == null) {
                        tablaDecision.getTabla().get(i).set(posCausa, resultadoEsperado);
                        insertePrimero = true;
                    }else{ //pos ya tenia valor
                        if (!insertePrimero) { 
                            Vector nuevoVector = new Vector(grafo.getCantidadCausas().intValue() + 1);    //se suma el efecto a la cantidad de causas
                            
                            for(int k=0; k < grafo.getCantidadCausas().intValue(); k++){
                                nuevoVector.addElement(null); //seteamos cada posicion
                            }

                            nuevoVector.addElement(1); //el efecto

                            nuevoVector.set(posCausa, resultadoEsperado);
                            //agregamos vector a la tabla de decision
                            tablaDecision.getTabla().add(nuevoVector);
                            
                        }
                        break;
                    }
                }

            } else {

                Vector vector = new Vector(grafo.getCantidadCausas().intValue() + 1);    //se suma el efecto a la cantidad de causas
                
                Integer posCausa = nodo.getCodigoN(); 
                //en la posicion de la causa (-1) cargo el valor del nodo Ejemplo: Causa1 = pos = 1-1 = 0
                for(int k=0; k < grafo.getCantidadCausas().intValue(); k++)vector.addElement(null);
                vector.addElement(1);

                vector.set(posCausa, resultadoEsperado);
                //agregamos vector a la tabla de decision
                tablaDecision.getTabla().add(vector);

            }
        }

    }


    public static ArrayList<Nodo> buscarEfectos(Grafo g){
        ArrayList<Nodo> listaEfectos = new ArrayList<Nodo>();
        if (g.getNodos().size() > 0) {
            for (Nodo nodo : g.getNodos()) {
                if ("efecto".equalsIgnoreCase(nodo.getTipo()) ) {
                    listaEfectos.add(nodo);
                }
            }
        }
        return listaEfectos;
    }

    public static Integer buscarCausas(Grafo g){
        Integer cantCausas = 0;
        if (g.getNodos().size() > 0) {
            for (Nodo nodo : g.getNodos()) {
                if ("causa".equalsIgnoreCase(nodo.getTipo()) ) {
                    cantCausas++;
                }
            }
        }
        return cantCausas;
    }


//    public static void imprimirTabla(TablaDecision t) {
//        //String [] causas = {"C","A","B","D","E","F", "e1"};
//        //String [] causas = {"D","E","F","C","A","B","e1"};
//        //String [] causas = {"c1","c2","c5","c3","c4","e1"};
//        //String [] causas = {"c3","c1","c2","c4","c5","c6","c7","c8","e1"};
//        //String [] causas = {"c1","c2","c3","e1"};
//        String [] causas = {"c7","c8","c9","c10","e5"};
//        //todas las columnas de la tabla tienen el mismo tamaño
//        for (int k = 0; k < t.getTabla().get(0).size(); k++) {
//            System.out.print(causas[k] + "\t");
//            for (int f = 0; f < t.getTabla().size(); f++) {
//                System.out.print(t.getTabla().get(f).get(k) + "\t");
//            }
//            System.out.print("\n");
//        }
//
//    }

    public static void imprimirTabla(TablaDecision t) {
        for (int k = 0; k < t.getTabla().get(0).size(); k++) {
            System.out.print(t.getCausasEfecto().get(k) + "\t");
            for (int f = 0; f < t.getTabla().size(); f++) {
                System.out.print(t.getTabla().get(f).get(k) + "\t");
            }
            System.out.print("\n");
        }
        System.out.print("------\n");
    }
    

    public static void completarTabla(TablaDecision tablaDecision) {
        
        //recorremos la tabla desde atras
        //recorremos el resto de los vectores menos el seleccionado
        
        for (int i = tablaDecision.getTabla().size()-1; i > tablaDecision.getCantidadVectores()-1; i--) {
            Vector vectorPrincipal = new Vector();
            for(int pos=0; pos < tablaDecision.getTabla().get(i).size(); pos++){
                vectorPrincipal.addElement(tablaDecision.getTabla().get(i).get(pos));
            }
            
            if (tieneValoresNulos(vectorPrincipal)) {
                
                for (int j = i-1; j > tablaDecision.getCantidadVectores()-1; j--) {
                    Vector vectorSecundario = tablaDecision.getTabla().get(j); //visito cada vector de la tabla
                    
                    //if (!tieneValoresNulos(vectorSecundario)) {
                        
                        boolean necesitoCrearVector = true; //indica si debo crear un nuevo vector
                        int causasNoNull=0;
                        for (int posCausa = 0; posCausa < vectorPrincipal.size(); posCausa++) {
                            //si en la posCausa del vector hay null y en posCausa del vector anterior existe valor, copiarlo
                            if (tablaDecision.getTabla().get(i).elementAt(posCausa) == null) {
                                
                                if (vectorSecundario.elementAt(posCausa) != null) {
                                    tablaDecision.getTabla().get(i).set(posCausa, vectorSecundario.get(posCausa));
                                    necesitoCrearVector = false;
                                }
                                
                            }else{ //si no es null en las mismas posiciones de los dos vectores
                                if (vectorSecundario.elementAt(posCausa) != null) {
                                    causasNoNull++;
                                }
                            }
                        }
                        
                        //si recorri todo el vector principal y ninguna causa era null: no necesito crear vector
                        if (causasNoNull == vectorPrincipal.size()) {
                            necesitoCrearVector = true;
                        }else{
                            //si el secundario tiene valores null, actualizo el principal
                            if(tieneValoresNulos(vectorSecundario)){
                                
                                vectorPrincipal = new Vector();
                                for(int pos=0; pos < tablaDecision.getTabla().get(i).size(); pos++){
                                    vectorPrincipal.addElement(tablaDecision.getTabla().get(i).get(pos));
                                }    
                                
                            }
                            
                            necesitoCrearVector = false;
                        }
                        
                        
                        if(necesitoCrearVector){
                            Vector vectorNuevo = new Vector(); 
                            for(int pos=0; pos < vectorPrincipal.size(); pos++){
                                vectorNuevo.addElement(vectorPrincipal.get(pos));
                            }
                            for(int k=0; k < vectorNuevo.size(); k++){
                                if(vectorNuevo.get(k) == null){
                                    vectorNuevo.setElementAt(vectorSecundario.get(k), k);
                                }
                            }
                            tablaDecision.getTabla().add(vectorNuevo);
                            //--; //resto uno porque despues de agregar el vector tiene mas elementos
                        }
                        
                    //}
                    
                }
            }


        }
    }


    public static boolean tieneValoresNulos(Vector vector){
        for(int i=0; i < vector.size(); i++){
            if(vector.get(i) == null)
                return true;
        }
        
        return false;
    }
    
    public static Vector obtenerVectorCausas(Grafo g) {
        Vector vectorCausas= new Vector();
        
        for(int k=0; k < g.getCantidadCausas().intValue()+1; k++){
            vectorCausas.addElement(null);
        }
        
        if (g.getNodos().size() > 0) {
            for (Nodo nodo : g.getNodos()) {
                if ("causa".equalsIgnoreCase(nodo.getTipo())) {
                    vectorCausas.set( nodo.getCodigoN(), "C"+nodo.getCodigoN() );
                }
            }
        }
        
        return vectorCausas;
    }

    
}

